﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using MultiViewer.Network;
using System.Threading;
using MultiViewer.Display;
using System.Diagnostics;
using MouseSpace;
using GlassSpace;
using SW = System.Windows;
using MultiViewer.Display.TiledImages;
using MouseKeyboardLibrary;
using System.Configuration;

namespace MultiViewer.Client
{
    public partial class MainWindow : MultiViewerWindow
    {
        private bool Glass;
        private MouseWindow windowMouse;
        private GlassWindow windowGlass;
        private int GridX;
        private int GridY;
        public MouseWindow WindowMouse
        {
            get { return this.windowMouse; }
            set { this.windowMouse = value; }
        }
        private bool cursor;
        public bool MousePressOn
        {
            get { return this.cursor; }
            set { this.cursor = value; }
        }
        private TraceViewer traceViewer;
        private TiledImageViewer imageViewer;

        public MainWindow()
        {
            InitializeComponent();
            windowMouse = new MouseWindow();
            windowGlass = new GlassWindow();
            windowMouse.Visibility = Visibility.Hidden;
            windowGlass.Visibility = Visibility.Hidden;
            traceViewer = new TraceViewer();
            Content = traceViewer;
#if DEBUG
            Trace.Listeners.Add(traceViewer.Listener);
#else
            Debug.Listeners.Add(traceViewer.Listener);
#endif
            Trace.WriteLine("MultiViewer.Client is starting...");
            Debug.WriteLine("MultiViewer is built in Debug mode");

            ThreadPool.SetMaxThreads(32, 32);
        }

        protected override void OnClosed(EventArgs e)
        {
            if (Node != null)
            {
                if (Content is TiledImageViewer)
                    ((TiledImageViewer)Content).DetachNode();
                Node.Stop();
            }
            try
            {
                windowMouse.Close();
                windowGlass.Close();
            }
            catch { }
            base.OnClosed(e);
        }

        protected override void OnNodeConnected()
        {
            base.OnNodeConnected();
            Node.AddMessageHandler<ShutdownRequest>(Dispatcher, ShutdownHandler);
            Node.AddNotificationHandler<MouseNotification>(MouseNotificationHandler);
            Node.AddNotificationHandler<MouseClickNotification>(MouseClickNotificationHandler);
            Node.AddNotificationHandler<MouseWheelNotification>(Dispatcher, MouseWheelNotificationHandler);
        }

        private void BeginInvoke<T>(NotificationHandler<T> handler, T argument)
        {
            Dispatcher.BeginInvoke(new SyncInvoker(delegate()
            {
                try
                {
                    handler(argument);
                }
                catch (Exception exc)
                {
                    Trace.WriteLine("Exception in " + typeof(T).Name + " handler: " + exc.Message);
                }
            }));
        }

        protected override void InitDisplayHandler(InitDisplayRequest request, HandlerResult<object> result)
        {
            Glass = request.Glass;
            base.InitDisplayHandler(request, null);
            if (request.Fullscreen)
            {
                WindowStyle = WindowStyle.None;
                WindowState = WindowState.Maximized;
                Topmost = true;
            }
            else
            {
                // TODO: Transform from pixels to WPF units
                Width = request.Width;
                Height = request.Height;
            }
            if (!Glass)
            {
                windowMouse.Width = request.CursorWidth;
                windowMouse.Height = request.CursorHeight;
            }
            else
            {
                windowGlass.Width = request.GlassWidth;
                windowGlass.Height = request.GlassHeight;
                windowGlass.Lens.Center = new Point(request.GlassWidth / 2 - 1, request.GlassHeight / 2 - 1);
                windowGlass.Lens.RadiusX = request.GlassWidth / 2 - 2;
                windowGlass.Lens.RadiusY = request.GlassHeight / 2 - 2;
            }
            MousePressOn = request.SimulateMouseEvents;
            GridX = request.GridWidth;
            GridY = request.GridHeight;
            imageViewer = new TiledImageViewer();
            Content = imageViewer;
            imageViewer.AttachNode(Node);
            result.SetSuccess(true);
        }

        private void ShutdownHandler(ShutdownRequest request, HandlerResult<object> result)
        {
            Close();
            result.SetSuccess(true);
        }

        private void MouseNotificationAsyncHandler(MouseNotification e)
        {
            BeginInvoke(MouseNotificationHandler, e);
        }
        private void mouseSim(Window window,MouseNotification e)
        {
            if (e.X != -10000)
            {
                if (this.MousePressOn) MouseKeyboardLibrary.MouseSimulator.X = (int)(e.X + this.Left);
                if (this.MousePressOn) MouseKeyboardLibrary.MouseSimulator.Y = (int)(e.Y + this.Top);
                window.Visibility = Visibility.Visible;
                window.Left = e.X + this.Left;
                window.Top = e.Y + this.Top;
                window.Topmost = true;
                window.Focus();
                window.Show();
            }
            else
            {
                if (window != null) window.Visibility = Visibility.Hidden;
            }
        }
        private void SetCursor(Window window, MouseNotification e)
        {
            try
            {
                // TODO: handle mouse events properly
                if (!window.Dispatcher.CheckAccess())
                {
                    this.Dispatcher.Invoke(
                      System.Windows.Threading.DispatcherPriority.Normal,
                      new Action(
                        delegate()
                        {
                            mouseSim(window, e);
                        }
                    ));
                }
                else
                {
                    mouseSim(window, e);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
        //этот метод вынесен, потому что в классе Node к нему не достаточно доступа
        // можно заменить директиву protected на public там, и использовать его оттуда
        protected static string GetParameter(string longName, string shortName, params string[] args)
        {
            string key = "/" + shortName + "=";
            if (args.Any(p => p.StartsWith(key)))
            {
                return args.Last(p => p.StartsWith(key)).Substring(shortName.Length + 2);
            }
            key = "/" + longName + "=";
            if (args.Any(p => p.StartsWith(key)))
            {
                return args.Last(p => p.StartsWith(key)).Substring(longName.Length + 2);
            }
            AppSettingsReader apr = new AppSettingsReader();
            try
            {
                return (string)apr.GetValue(longName, typeof(string));
            }
            catch // Value in config file is not found
            {
                return null;
            }
        }
        //метод для получения необходимого прямоугольника для лупы
        private MultiViewer.Rect GetGlass(VirtualEarthImage vei, MouseNotification e)
        {
            MultiViewer.Rect Temp = imageViewer.NodeVisible;
            double Xprop = Temp.Width / this.ActualWidth;
            double Yprop = Temp.Height / this.ActualHeight;
            double X = (vei.TileSize.Width) * this.ActualHeight / vei.TileSize.Height;
            double dif = this.ActualWidth - X;
            AppSettingsReader apr = new AppSettingsReader();
            int nodeID = Int32.Parse(GetParameter("multiViewerNodeID", "mvid", ((MultiViewerApplication)Application.Current).Arguments));
            int NodeYinGrid = (int)Math.Round((decimal)((nodeID - 1) / GridX)) + 1;
            int NodeXinGrid = (int)(nodeID - GridX * (NodeYinGrid - 1));
            double mouseX;
            double mouseY;
            if (dif > 0)
            {
                if (GridX == 1)
                {
                    mouseX = (e.X - dif) *
                    (this.ActualWidth / (this.ActualWidth - 2 * dif)) *
                    Xprop;
                }
                else if (NodeXinGrid == 1)
                {
                    mouseX = (e.X - dif) *
                    (this.ActualWidth / (this.ActualWidth - dif)) *
                    Xprop;
                }
                else if (NodeXinGrid == GridX)
                {
                    mouseX = e.X *
                    (this.ActualWidth / (this.ActualWidth - dif)) *
                    Xprop;
                }

                else
                {
                    mouseX = e.X * Xprop;
                }
                mouseY = (this.ActualHeight - e.Y) * Yprop;
            }
            else
            {
                X = (vei.TileSize.Height) * this.ActualWidth / vei.TileSize.Width;
                dif = this.ActualHeight - X;
                if (GridY == 1)
                {
                    mouseY = (this.ActualHeight - e.Y - dif) *
                    (this.ActualHeight / (this.ActualHeight - 2 * dif)) *
                    Yprop;
                }
                else if (NodeYinGrid == GridY)
                {
                    mouseY = (this.ActualHeight - e.Y) *
                    (this.ActualHeight / (this.ActualHeight - dif)) *
                    Yprop;
                }
                else if (NodeYinGrid == 1)
                {
                    mouseY = (this.ActualHeight - e.Y - dif) *
                    (this.ActualHeight / (this.ActualHeight - dif)) *
                    Yprop;
                }
                else
                {
                    mouseY = (this.ActualHeight - e.Y) * Yprop;
                }
                mouseX = e.X * Xprop;
            }
            MultiViewer.Rect Res = new Rect(Temp.Left + mouseX, Temp.Bottom + mouseY - windowGlass.GlassCanvas.ActualHeight / 2 * Yprop, Temp.Right - (Temp.Width - mouseX) + windowGlass.GlassCanvas.ActualWidth / 2 * Xprop, Temp.Top - (Temp.Height - mouseY));
            //MultiViewer.Rect Res = new Rect(Temp.Left + mouseX, Temp.Bottom + mouseY - windowGlass.ActualHeight / 2 * Yprop, Temp.Right - (Temp.Width - mouseX) + windowGlass.ActualWidth / 2 * Xprop, Temp.Top - (Temp.Height - mouseY));
            double Width = Res.Width;
            double Height = Res.Height;
            Res.Left = Res.Left - Width / 2;
            Res.Right = Res.Right - Width / 2;
            Res.Top = Res.Top + Height / 2;
            Res.Bottom = Res.Bottom + Height / 2;
            return Res;
            //return Res;
        }
        private void NotificateMouseOrGlass(MouseNotification e)
        {
            try
            {
                if (!(e.X == -10000))
                {
                    VirtualEarthImage vei = new VirtualEarthImage();
                    MultiViewer.Rect rect = GetGlass(vei, e);
                    if (!((rect.Bottom < vei.EntireImage.Bottom) || (rect.Top > vei.EntireImage.Top) || (rect.Left < vei.EntireImage.Left) || (rect.Right > vei.EntireImage.Right)))
                    {
                        windowGlass.GlassCanvas.ClipToBounds = true;
                        vei.Render(windowGlass.GlassCanvas, new SW.Rect(new Size(windowGlass.GlassCanvas.ActualWidth, windowGlass.GlassCanvas.ActualHeight)), rect);
                    }
                }
                e.X -= this.windowGlass.GlassCanvas.ActualWidth/2;
                e.Y -= this.windowGlass.GlassCanvas.ActualHeight/2;
                SetCursor(windowGlass, e);
            }
            catch(Exception ex) 
            {
                MessageBox.Show(ex.Message);
            }
        }
        private void MouseNotificationHandler(MouseNotification e)
        {
            if (Glass)
            {
                if (!windowGlass.Dispatcher.CheckAccess())
                {
                    this.Dispatcher.Invoke(
                      System.Windows.Threading.DispatcherPriority.Normal,
                      new Action(
                        delegate()
                        {
                            NotificateMouseOrGlass(e);
                        }
                    ));
                }
                else
                {
                    NotificateMouseOrGlass(e);
                }
            }
            else SetCursor(windowMouse, e);
        }


        private void MouseCLickNotificationAsyncHandler(MouseClickNotification e)
        {
            BeginInvoke(MouseClickNotificationHandler, e);
        }

        private void MouseWheelNotificationHandler(MouseWheelNotification e)
        {
            MouseKeyboardLibrary.MouseSimulator.MouseWheel(e.Delta);
        }

        private void Simulate(MouseClickNotification e)
        {
            if (e.Button == MultiViewer.Display.MouseButton.LeftDown)
            {
                MouseKeyboardLibrary.MouseSimulator.X = (int)(e.X + this.Left);
                MouseKeyboardLibrary.MouseSimulator.Y = (int)(e.Y + this.Top);
                MouseKeyboardLibrary.MouseSimulator.MouseDown(MouseKeyboardLibrary.MouseButton.Left);
            }
            else if (e.Button == MultiViewer.Display.MouseButton.LeftUp)
            {
                MouseKeyboardLibrary.MouseSimulator.X = (int)(e.X + this.Left);
                MouseKeyboardLibrary.MouseSimulator.Y = (int)(e.Y + this.Top);
                MouseKeyboardLibrary.MouseSimulator.MouseUp(MouseKeyboardLibrary.MouseButton.Left);
            }
            else if (e.Button == MultiViewer.Display.MouseButton.RightDown)
            {
                MouseKeyboardLibrary.MouseSimulator.X = (int)(e.X + this.Left);
                MouseKeyboardLibrary.MouseSimulator.Y = (int)(e.Y + this.Top);
                MouseKeyboardLibrary.MouseSimulator.MouseDown(MouseKeyboardLibrary.MouseButton.Right);
            }
            else if (e.Button == MultiViewer.Display.MouseButton.RightUp)
            {
                MouseKeyboardLibrary.MouseSimulator.X = (int)(e.X + this.Left);
                MouseKeyboardLibrary.MouseSimulator.Y = (int)(e.Y + this.Top);
                MouseKeyboardLibrary.MouseSimulator.MouseUp(MouseKeyboardLibrary.MouseButton.Right);
            }
        }
        private void MouseClickNotificationHandler(MouseClickNotification e)
        {
            try
            {
                if (!this.Dispatcher.CheckAccess())
                {
                    windowMouse.Dispatcher.Invoke(
                      System.Windows.Threading.DispatcherPriority.Normal,
                      new Action(
                        delegate()
                        {
                            Simulate(e);
                        }
                        ));
                }
                else
                {
                    Simulate(e);
                }

            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }       
}
